package org.afraid.freedns.util;

import java.util.concurrent.TimeUnit;

import lombok.extern.slf4j.Slf4j;
import okhttp3.Callback;
import okhttp3.FormBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Request.Builder;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;

/**
 * OKHttp网络访问工具类
 * 
 * @author jianggq
 * @date 2018年7月24日下午8:00:45
 */
@Slf4j
public class HttpClient {

	public static final int DEFAULT_TIMEOUT_SECONDS = 30;

	private HttpClient() {

	}

	private static final OkHttpClient client;

	static {

		client = new OkHttpClient.Builder()//
				.connectTimeout(DEFAULT_TIMEOUT_SECONDS, TimeUnit.SECONDS)//
				.writeTimeout(DEFAULT_TIMEOUT_SECONDS, TimeUnit.SECONDS)//
				// .cookieJar(new RedisCookieJarImpl()) 高可用环境下，存储到Redis
				.cookieJar(new CookieJarImpl(new MemoryCookieStore())) // TODO 演示demo,这里就是单机存储
				.build();

	}

	private static String appUserAgent;

	/**
	 * user-agent
	 *
	 * @param context
	 *            context
	 * @return String
	 */
	private static String getUserAgent() {
		if (appUserAgent == null || "".equals(appUserAgent)) {

			StringBuilder ua = new StringBuilder();

			// 如果有的话，此处可以添加http://freedns.afraid.org/ 特有的agent信息

			appUserAgent = ua.toString();
		}

		return appUserAgent;
	}

	public static Builder getRequestBuilder(String url) {

		Builder requestBuilder = new Builder();
		requestBuilder.addHeader("User-Agent", getUserAgent());
		requestBuilder.url(url);

		return requestBuilder;
	}

	/**
	 * @param context
	 *            context
	 * @param url
	 *            url
	 * @return String
	 * @throws Exception
	 *             Exception
	 */
	public static String get(String url) throws Exception {
		return get(url, null);
	}

	/**
	 * 同步get请求
	 *
	 * @param context
	 *            context
	 * @param url
	 *            url
	 * @param builder
	 *            builder
	 * @return String
	 * @throws Exception
	 *             Exception
	 */
	public static String get(String url, FormBody.Builder builder) throws Exception {
		return responseBodyToString(execute(buildGet(url, builder)).body());
	}

	/**
	 * 异步get请求
	 *
	 * @param context
	 *            context
	 * @param url
	 *            url
	 * @throws Exception
	 *             Exception
	 */
	public static void asyncGet(String url, Callback callBack) throws Exception {
		asyncGet(url, null, callBack);
	}

	/**
	 * 异步get请求
	 *
	 * @param context
	 *            context
	 * @param url
	 *            url
	 * @param builder
	 *            builder
	 * @throws Exception
	 *             Exception
	 */
	public static void asyncGet(String url, FormBody.Builder builder, Callback callBack) throws Exception {
		enqueue(buildGet(url, builder), callBack);
	}

	/**
	 * 同步post请求
	 *
	 * @param context
	 *            context
	 * @param url
	 *            url
	 * @param builder
	 *            builder
	 * @return String
	 * @throws Exception
	 *             Exception
	 */
	public static String post(String url, FormBody.Builder builder) throws Exception {
		return post(url, builder.build());
	}

	/**
	 * 同步post请求
	 *
	 * @param context
	 *            context
	 * @param url
	 *            url
	 * @param requestBody
	 *            requestBody
	 * @return String
	 * @throws Exception
	 *             Exception
	 */
	public static String post(String url, RequestBody requestBody) throws Exception {
		return responseBodyToString(execute(buildPost(url, requestBody)).body());
	}

	/**
	 * 异步post请求
	 *
	 * @param context
	 *            context
	 * @param url
	 *            url
	 * @param builder
	 *            builder
	 * @throws Exception
	 *             Exception
	 */
	public static void asyncPost(String url, FormBody.Builder builder, Callback callBack) throws Exception {
		asyncPost(url, builder.build(), callBack);
	}

	/**
	 * 异步post请求
	 *
	 * @param context
	 *            context
	 * @param url
	 *            url
	 * @param requestBody
	 *            requestBody
	 * @throws Exception
	 *             Exception
	 */
	public static void asyncPost(String url, RequestBody requestBody, Callback callBack) throws Exception {
		enqueue(buildPost(url, requestBody), callBack);
	}

	/**
	 * 构建get请求
	 *
	 * @param context
	 *            context
	 * @param url
	 *            url
	 * @return Request
	 * @throws Exception
	 *             Exception
	 */
	public static Request buildGet(String url) throws Exception {
		return buildGet(url, null);
	}

	/**
	 * 构建get请求
	 *
	 * @param context
	 *            context
	 * @param url
	 *            url
	 * @param builder
	 *            builder
	 * @return Request
	 * @throws Exception
	 *             Exception
	 */
	private static Request buildGet(String url, FormBody.Builder builder) throws Exception {

		if (builder != null)
			url += "?" + formatParams(builder.build());

		log.info("GET：{}", url);

		return getRequestBuilder(url).build();
	}

	/**
	 * 构建post请求
	 *
	 * @param context
	 *            context
	 * @param url
	 *            url
	 * @param requestBody
	 *            requestBody
	 * @return Request
	 * @throws Exception
	 *             Exception
	 */
	private static Request buildPost(String url, RequestBody requestBody) throws Exception {

		log.info("POST：{}", url);
		log.info("BODY：{}", formatFormBody(requestBody));

		return getRequestBuilder(url).post(requestBody).build();
	}

	/**
	 * 不会开启异步线程。
	 *
	 * @param request
	 *            request
	 * @return Response
	 * @throws Exception
	 *             Exception
	 */
	private static Response execute(Request request) throws Exception {
		return client.newCall(request).execute();
	}

	/**
	 * 开启异步线程访问网络
	 *
	 * @param request
	 *            request
	 * @param callBack
	 *            callBack
	 */
	private static void enqueue(Request request, Callback callBack) {
		client.newCall(request).enqueue(callBack);
	}

	public static String responseBodyToString(ResponseBody responseBody) throws Exception {
		if (responseBody != null)
			return responseBody.string();

		return null;
	}

	/**
	 * @param requestBody
	 *            requestBody
	 * @return String
	 */
	private static String formatParams(RequestBody requestBody) {

		boolean isFirst = false;
		StringBuilder sBuilder = new StringBuilder();

		FormBody formBody = (FormBody) requestBody;
		int bodySize = formBody.size();

		for (int i = 0; i < bodySize; i++) {

			if (isFirst = !isFirst)
				sBuilder.append("&");

			sBuilder.append(formBody.encodedName(i));
			sBuilder.append("=");
			sBuilder.append(formBody.encodedValue(i));
		}

		return sBuilder.toString();
	}

	/**
	 * @param requestBody
	 *            requestBody
	 * @return String
	 */
	private static String formatFormBody(RequestBody requestBody) {

		if (!(requestBody instanceof FormBody))
			return "";

		StringBuilder sBuilder = new StringBuilder();

		FormBody formBody = (FormBody) requestBody;
		int bodySize = formBody.size();

		for (int i = 0; i < bodySize; i++) {
			sBuilder.append(formBody.encodedName(i));
			sBuilder.append("=");
			sBuilder.append(formBody.encodedValue(i));
			sBuilder.append(", ");
		}

		if (bodySize > 0)
			sBuilder.delete(sBuilder.length() - 2, sBuilder.length());

		return sBuilder.toString();
	}

}
